
#include "iap2.h"
#include "udc.h"
#include "iic.h"
#include "io_api.h"
#include "common_api.h"
#include "io_mgr.h"

static uint8_t auth_init_data[AUTH_INIT_DATA_LEN] = {0xff,0x55,0x02,0x00,0xee,0x10};
static uint8_t sync_verify_packet[13]={IAP2_LINK_VERSION,
                                             MAX_NUM_OUTSTANDING_PACKET,
                                             MAX_PACKET_LEN/256,
                                             MAX_PACKET_LEN%256,
                                             RETRANSMISSION_TIMEOUT/256,
                                             RETRANSMISSION_TIMEOUT%256,
                                             CUMULATIVE_ACK_TIMEOUT/256,
                                             CUMULATIVE_ACK_TIMEOUT%256,
                                             MAX_NUM_RETRANSMISSION,
                                             MAX_CUMULATIVE_ACK,
                                             CONTROL_SESSION_ID,
                                             CONTROL_SESSION_TYPE,
                                             SESSION_VERSION};

/*static uint8_t id_info_message_sent_by_accessory[] = 
{
    0x00, 12, 0x00, 0x06, 0xea, 0x02, 0xae, 0x00, 0xae, 0x02, 0xae, 0x03
};*/

static uint8_t id_info_message_sent_by_accessory[] = 
{
    0x00, 12, 0x00, 0x06, 0xae, 0x00, 0xae, 0x02, 0xae, 0x03, 0xea, 0x02
};

static uint8_t id_info_message_sent_by_accessory_without_launch[] = 
{
    0x00, 10, 0x00, 0x06, 0xae, 0x00, 0xae, 0x02, 0xae, 0x03
};

/*static uint8_t id_info_message_rec_from_device[] =
{
    0x00, 10, 0x00, 0x07, 0xea, 0x00, 0xea, 0x01, 0xae, 0x01
};*/

static uint8_t id_info_message_rec_from_device[] =
{
    0x00, 6, 0x00, 0x07, 0xae, 0x01
};

static uint8_t id_info_power_source_type[] = 
{
    0x00, 0x05, 0x00, 0x08, 0x02
};

/*static uint8_t id_info_max_current_drawn[] =
{
    0x00, 0x06, 0x00, 0x09, 0x00, 20
};*/

static uint8_t id_info_max_current_drawn[] =
{
    0x00, 0x06, 0x00, 0x09, 0x00, 0
};

static uint8_t id_info_supported_accessory_protocol[] = 
{
    0x00, 39, 0x00, 0x0a, 0x00, 5, 0x00, 0x00, 0x01, 0x00, 19,
    0x00, 0x01,
    'c', 'o', 'm', '.', 'm', 'o', 'p', 'h', 'i', 'e', '.', 'j', 'p', 'm',
    0x00, 0x00, 5, 0x00, 0x02, 0x01, 0x00, 6, 0x00, 0x03, 0x00, 0x01
};

static uint8_t id_info_host_trans_component[] =
{
    0x00, 31, 0x00, 16, 0x00, 6, 0x00, 0x00, 0x00, 0x01, 0x00, 17, 
    0x00, 0x01,
    'I', 'A', 'P', '2', '-', 'U', 'S', 'B', 'H', 'O', 'S', 'T',
    0x00, 0x00, 4, 0x00, 0x02
};


COMPILER_ALIGNED(4) uint8_t iap2_in_buffer[1024 + 30];
COMPILER_ALIGNED(4) uint8_t iap2_out_buffer[1024 + 30];

uint8_t *iap2_trans_in_ptr = NULL;

uint8_t g_iap2_status = IAP2_IDLE;
uint8_t iap2_packet_sequence_num = 0;
uint8_t iap2_packet_ack_num = 0;
uint16_t certification_tx_len = 0; 
uint8_t id_info_str[32];

static bool start_iap2_sequence(uint8_t *data_ptr, uint16_t data_len);
//static void iap2_in_proc(void);
//static void iap2_out_proc(void);
//static bool receive_iap2_pkt(uint8_t *buff_ptr, uint16_t data_len);
static bool verify_init_sequence(uint32_t auth_data_len);
//static void get_init_sequence(void);
static void get_sync_packet(void);
static uint8_t iap2_get_checksum(uint8_t *data_ptr, uint16_t len);
static bool check_sync_packet(uint32_t sync_pkt_len);
static void get_iap2_ack(void);
static bool verify_iap2_certification(uint32_t authe_certification_len);
static bool check_challenge_response(uint32_t challenge_response_len);
static bool check_start_identification(uint32_t start_identification_len);
static uint16_t make_id_info_str(uint8_t *src, uint8_t *dst, uint8_t index);
static bool start_power_updates(void);
static void stop_power_updates(void);
static bool check_iap_ack(uint8_t a, uint8_t b);
#define check_authen_success() check_iap_ack(0xaa, 0x05)
#define check_indentification() check_iap_ack(0x1d, 0x02)
static void set_charge_setting(uint8_t charge_mode, uint8_t charge_flag);
static void start_apple_lunch(void);

bool b_reset_flag = false;

bool iap2_set_charge_config(uint8_t charge_config)
{
    bool b_ret = false;
    uint32_t rev_len = 0;
    
    if(g_iap2_status == IAP2_IDLE)
    {
        set_charge_setting(charge_config, 1);
        do
        {
            b_ret = start_iap2_sequence(iap2_in_buffer, START_CHARGE_SET_LEN);
        }while(b_ret == false); 
        
        do
        {
            b_ret = receive_iap2_pkt(iap2_out_buffer, &rev_len);
        }while((b_ret == false));
        
        if(rev_len != IAP2_ACK_LEN)
        {
            return false;
        }
        
        else
        {
            iap2_packet_ack_num = iap2_out_buffer[5];
            g_iap2_status = IAP2_IDLE;
        }              
    }
    
    return true;
}

bool iap2_start_sequence(void)
{
    uint32_t rev_len = 0;
  
    if(g_jpm_start_mfi == MFI_AUTH_START)
    {
        if(sysclk_run_fastest == false)
        {
            enter_work_mode();
        }
        switch(g_iap2_status)
        {
        case IAP2_IDLE:
            common_mem_cpy(auth_init_data, iap2_in_buffer, AUTH_INIT_DATA_LEN);
            g_iap2_status = IAP2_AUTHEN_INITIALIZATION;
            break;
            
        case IAP2_AUTHEN_INITIALIZATION:                      
            if(start_iap2_sequence(iap2_in_buffer, AUTH_INIT_DATA_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_SYNC_PRE;
            }                                  
            break;
            
        case IAP2_SYNC_PRE:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(!verify_init_sequence(rev_len))
            {
                return false;
            }
            else
            {
                get_sync_packet();
                g_iap2_status = IAP2_SYNC_SEND;
            }
            break;
            
        case IAP2_SYNC_SEND:           
            if(start_iap2_sequence(iap2_in_buffer, SYNC_PACKET_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_SYNC_COMPLETE;
            } 
            break;
            
        case IAP2_SYNC_COMPLETE:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(!check_sync_packet(rev_len))
            {
                return false;
            }
            else
            {
                get_iap2_ack();
                g_iap2_status = IAP2_SYNC_OVER;
            }
            break;
            
        case IAP2_SYNC_OVER:
            if(start_iap2_sequence(iap2_in_buffer, IAP2_ACK_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_REQ_AUTHEN_CERTIFICATION;
            } 
            break;
            
        case IAP2_REQ_AUTHEN_CERTIFICATION:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(!verify_iap2_certification(rev_len))
            {
                return false;
            }
            else
            {
                iap2_trans_in_ptr = &(iap2_in_buffer[0]);
                g_iap2_status = IAP2_SEND_AUTHEN_CERTIFICATION;
            }
            break;
            
        case IAP2_SEND_AUTHEN_CERTIFICATION:
            if(certification_tx_len > 0)
            {
                if(certification_tx_len > IAP2_PACKET_LEN)
                {
                    if(start_iap2_sequence(iap2_trans_in_ptr, IAP2_PACKET_LEN) == false)
                    {
                        break;
                    }
                    else
                    {
                        iap2_trans_in_ptr += IAP2_PACKET_LEN;
                        certification_tx_len -= IAP2_PACKET_LEN;
                    }
                }
                else
                {
                    if(start_iap2_sequence(iap2_trans_in_ptr, certification_tx_len) == false)
                    {
                        break;
                    }
                    else
                    {
                        iap2_trans_in_ptr += certification_tx_len;
                        certification_tx_len = 0;
                    }
                }
            }
            else
            {
                g_iap2_status = IAP2_REC_AUTHEN_CERTIFICATION_RESPONSE;
            }
            break;
            
        case IAP2_REC_AUTHEN_CERTIFICATION_RESPONSE:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(rev_len != IAP2_ACK_LEN)
            {
                return false;
            }
            else
            {
                g_iap2_status = IAP2_REC_CHALLENGE_RESPONSE;
            }
            break;
            
        case IAP2_REC_CHALLENGE_RESPONSE:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(!check_challenge_response(rev_len))
            {
                return false;
            }
            else
            {
                g_iap2_status = IAP2_SEND_CHALLENGE;
            }
            break;
            
        case IAP2_SEND_CHALLENGE:
            if(start_iap2_sequence(iap2_in_buffer, certification_tx_len) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_ACK_CHALLENGE;
            }
            break;
            
        case IAP2_ACK_CHALLENGE:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(rev_len != IAP2_ACK_LEN)
            {
                return false;
            }
            else
            {
                if(b_iOS6 == true)
                {
                    g_iap2_status = IAP2_REC_START_IDENTIFICATION;
                }
                else
                {
                    g_iap2_status = IAP2_REC_AUTHEN_SUCCESS;
                }
            }
            break;
            
        case IAP2_REC_AUTHEN_SUCCESS:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if((!check_authen_success()) || (rev_len != AUTHEN_SUCCESS_LEN))
            {
                return false;               
            }
            else
            {                
                g_iap2_status = IAP2_ACK_AUTHEN_SUCCESS;
            }
            break;
            
        case IAP2_ACK_AUTHEN_SUCCESS:
            if(start_iap2_sequence(iap2_in_buffer, IAP2_ACK_LEN) == false)
            {
                break;
            }
            else
            {
                if(b_iOS6 == true)
                {
                    g_iap2_status = IAP2_REC_IDENTIFICATION_ACCEPT;
                }
                else
                {
                    g_iap2_status = IAP2_REC_START_IDENTIFICATION;
                }
            }
            break;
            
        case IAP2_REC_START_IDENTIFICATION:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(!check_start_identification(rev_len))
            {
                return false;               
            }
            else
            {                
                iap2_trans_in_ptr = &(iap2_in_buffer[0]);               
                g_iap2_status = IAP2_SEND_IDENTIFICATION;
            }
            break;
            
        case IAP2_SEND_IDENTIFICATION:
            if(certification_tx_len > 0)
            {
                if(certification_tx_len > IAP2_PACKET_LEN)
                {
                    if(start_iap2_sequence(iap2_trans_in_ptr, IAP2_PACKET_LEN) == false)
                    {
                        break;
                    }
                    else
                    {
                        iap2_trans_in_ptr += IAP2_PACKET_LEN;
                        certification_tx_len -= IAP2_PACKET_LEN;
                    }
                }
                else
                {
                    if(start_iap2_sequence(iap2_trans_in_ptr, certification_tx_len) == false)
                    {
                        break;
                    }
                    else
                    {
                        iap2_trans_in_ptr += certification_tx_len;
                        certification_tx_len = 0;
                    }
                }
            }
            else
            {
                g_iap2_status = IAP2_ACK_IDENTIFICATION;
            }
            break;
            
        case IAP2_ACK_IDENTIFICATION:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(rev_len != IAP2_ACK_LEN)
            {
                return false;               
            }
            else
            {
                if(b_iOS6 == true)
                {
                    g_iap2_status = IAP2_REC_AUTHEN_SUCCESS;
                }
                else
                {
                    g_iap2_status = IAP2_REC_IDENTIFICATION_ACCEPT;
                }
            }
            break;
            
        case IAP2_REC_IDENTIFICATION_ACCEPT:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if((check_indentification() == false) || (rev_len != IDENTIFICATION_ACCETP_LEN))
            {
                return false;               
            }
            else
            {                
                g_iap2_status = IAP2_ACK_IDENTIFICATION_ACCEPT;
            }
            break;
            
        case IAP2_ACK_IDENTIFICATION_ACCEPT:
            if(start_iap2_sequence(iap2_in_buffer, IAP2_ACK_LEN) == false)
            {
                break;
            }
            else
            {
                if(b_iOS6 == true)
                {
                    //iap2_packet_ack_num = iap2_out_buffer[5];
                    //g_iap2_status = IAP2_IDLE;
                    //g_jpm_start_mfi = MFI_AUTH_COMPLETE;
                    if((ex_status.ex_power_type == P_SRC_PC_PRE) && (b_need_to_verify == true))
                    {
                        start_apple_lunch();
                        g_iap2_status = IAP2_SEND_APPLE_LUNCH;
                    }
                    else
                    {
                        iap2_packet_ack_num = iap2_out_buffer[5];
                        g_iap2_status = IAP2_IDLE;
                        g_jpm_start_mfi = MFI_AUTH_COMPLETE;
                    }
                }
                else
                {
                    start_power_updates();
                    g_iap2_status = IAP2_SEND_START_POWER_UPDATE;
                }
            }
            break;
            
        case IAP2_SEND_START_POWER_UPDATE:
            if(start_iap2_sequence(iap2_in_buffer, START_POWER_UPDATES_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_REC_START_POWER_UPDATE_ACK;
            }
            break;
            
        case IAP2_REC_START_POWER_UPDATE_ACK:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(rev_len != IAP2_ACK_LEN)
            {
                return false;
            }
            else
            {
                iap2_packet_ack_num = iap2_out_buffer[5];
                /*if(ex_status.ex_power_type == P_SRC_)
                {
                    set_charge_setting(CHARGE_MODE_FULL, 0);
                }
                else
                {
                    set_charge_setting(CHARGE_MODE_OFF, 0);
                }*/
                
                if((ex_status.ex_power_type == P_SRC_NONE) && (ex_status.switch_status != SWT_ON))
                {
                    set_charge_setting(CHARGE_MODE_OFF, 0);
                }
                else if((ex_status.ex_power_type == P_SRC_PC_PRE) || (ex_status.ex_power_type == P_SRC_0P5A))
                {
                    set_charge_setting(CHARGE_MODE_HALF, 0);
                }
                else
                {
                    set_charge_setting(CHARGE_MODE_FULL, 0);
                }
                
                g_iap2_status = IAP2_SEND_POWER_UPDATE;
            }
            break;
            
        case IAP2_SEND_POWER_UPDATE:
            if(start_iap2_sequence(iap2_in_buffer, START_CHARGE_SET_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_REC_POWER_UPDATE_ACK;
            }
            break;
            
        case IAP2_REC_POWER_UPDATE_ACK:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(rev_len != IAP2_ACK_LEN)
            {
                return false;
            }
            else
            {
                g_iap2_status = IAP2_REC_POWER_UPDATE_DATA;
            }
            break;
            
        case IAP2_REC_POWER_UPDATE_DATA:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(!check_iap_ack(0xae,0x01))
            {
                return false;
            }
            else
            {
                g_iap2_status = IAP2_ACK_POWER_UPDATE_DATA;
            }
            break;
            
        case IAP2_ACK_POWER_UPDATE_DATA:
            if(start_iap2_sequence(iap2_in_buffer, IAP2_ACK_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_REC_POWER_UPDATE_DATA_AGAIN;
            }
            break;
            
        case IAP2_REC_POWER_UPDATE_DATA_AGAIN:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(!check_iap_ack(0xae,0x01))
            {
                return false;
            }
            else
            {
                stop_power_updates();
                g_iap2_status = IAP2_SEND_STOP_POWER_UPDATE;
            }
            break;
            
        case IAP2_SEND_STOP_POWER_UPDATE:
            if(start_iap2_sequence(iap2_in_buffer, STOP_POWER_UPDATES_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_REC_STOP_POWER_UPDATE_ACK;
            }
            break;
            
        case IAP2_REC_STOP_POWER_UPDATE_ACK:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(rev_len != IAP2_ACK_LEN)
            {
                return false;
            }
            else
            {
                //iap2_packet_ack_num = iap2_out_buffer[5];
                //g_iap2_status = IAP2_IDLE;
                //g_jpm_start_mfi = MFI_AUTH_COMPLETE;
                if((ex_status.ex_power_type == P_SRC_PC_PRE) && (b_need_to_verify == true))
                {
                    start_apple_lunch();
                    g_iap2_status = IAP2_SEND_APPLE_LUNCH;
                }
                else
                {
                    iap2_packet_ack_num = iap2_out_buffer[5];
                    g_iap2_status = IAP2_IDLE;
                    g_jpm_start_mfi = MFI_AUTH_COMPLETE;
                }                
            }
            break;
            
        case IAP2_SEND_APPLE_LUNCH:
            if(start_iap2_sequence(iap2_in_buffer, SEND_APPLE_LUNCH_LEN) == false)
            {
                break;
            }
            else
            {
                g_iap2_status = IAP2_REC_APPLE_LUNCH_ACK;
            }
            break;
            
        case IAP2_REC_APPLE_LUNCH_ACK:
            if(!receive_iap2_pkt(iap2_out_buffer, &rev_len))
            {
                break;
            }
            if(rev_len != IAP2_ACK_LEN)
            {
                return false;
            }
            else
            {
                iap2_packet_ack_num = iap2_out_buffer[5];
                g_iap2_status = IAP2_IDLE;
                g_jpm_start_mfi = MFI_AUTH_COMPLETE; 
//                start_power_updates();
//                g_iap2_status = IAP2_SEND_START_POWER_UPDATE;
            }           
            break;
            
        default:
            return false;
            break;
        }
    }
    
    return true;
}

static bool start_iap2_sequence(uint8_t *data_ptr, uint16_t data_len)
{
    bool bret;
    bret = udd_ep_send_one_pkt((IAP2_EP_IN & 0x0f), data_ptr, data_len);
    if(bret == false)
    {
        return false;
    }
    
    if(b_iOS6 == true)
    {
        do
        {
            bret = udd_ep_send_one_pkt((INTR_EP_IN & 0x0f), NULL, 0);
        }while(bret == false);
    }
    return true;
}

//static bool receive_iap2_pkt(uint8_t *buff_ptr, uint16_t data_len)
//{
//    return udd_ep_receive(IAP2_EP_OUT & 0x0f, buff_ptr, data_len, data_len, IAP2_PACKET_LEN);
//}

/*static void iap2_in_proc(void)
{
    uint16_t send_len;
  
    if((g_iap2_status == REC_REQ_AUTHEN_CERTIFICATION) ||
       (g_iap2_status == REC_START_IDENTIFICATION) ||
         (g_iap2_status == AUTHEN_SUCCESS_ACK))
    {
        if(certification_tx_len > IAP2_PACKET_LEN)
        {           
            send_len = IAP2_PACKET_LEN;
            udd_ep_send_one_pkt((IAP2_EP_IN & 0x0f), iap2_trans_in_ptr, send_len);
            iap2_trans_in_ptr += IAP2_PACKET_LEN;
            certification_tx_len -= IAP2_PACKET_LEN;
        }
        else
        {
            send_len = certification_tx_len;
            certification_tx_len = 0;
            udd_disable_endpoint_interrupt((IAP2_EP_IN&0x0f));
            udd_disable_in_send_interrupt((IAP2_EP_IN&0x0f));
            udd_ep_send_one_pkt((IAP2_EP_IN & 0x0f), iap2_trans_in_ptr, send_len);          
        }

        if(certification_tx_len == 0)
        {
            g_iap2_status += 1;
        }
    }
}*/

/*static void iap2_out_proc(void)
{
    bool b_ret = true;
    uint32_t natural_rev_len;
    switch(g_iap2_status)
    {
    case REC_AUTHEN_INITIALIZATION:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, AUTH_INIT_DATA_LEN);
      b_ret &= verify_init_sequence();
      
      if(b_ret)
      {
          get_sync_packet();
          start_iap2_sequence(iap2_in_buffer, SYNC_PACKET_LEN);
          g_iap2_status = REC_SYNC_PACKET;
      }
      break;
      
    case REC_SYNC_PACKET:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, SYNC_PACKET_LEN);
      b_ret &= check_sync_packet();
      
      if(b_ret)
      {
          get_iap2_ack();
          start_iap2_sequence(iap2_in_buffer, IAP2_ACK_LEN);
          g_iap2_status = REC_REQ_AUTHEN_CERTIFICATION;
      }
      break;
      
    case REC_REQ_AUTHEN_CERTIFICATION:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, AUTHEN_CERTIFICATION_PACKET_LEN);
      b_ret &= verify_iap2_certification();
      
      if(b_ret)
      {
          if(certification_tx_len > IAP2_PACKET_LEN)
          {
              iap2_trans_in_ptr = &(iap2_in_buffer[0]);
              start_iap2_sequence(iap2_trans_in_ptr, certification_tx_len);
              iap2_trans_in_ptr += IAP2_PACKET_LEN;
              certification_tx_len -= IAP2_PACKET_LEN;
              udd_enable_endpoint_interrupt((IAP2_EP_IN&0x0f));
              udd_enable_in_send_interrupt((IAP2_EP_IN&0x0f));
          }
          else
          {
              start_iap2_sequence(iap2_in_buffer, certification_tx_len);
              g_iap2_status = REC_AUTHEN_CERTIFICATION_RESPONSE;
          }
      }
      break;
      
    case REC_AUTHEN_CERTIFICATION_RESPONSE:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IAP2_ACK_LEN);
      
      if(b_ret)
      {
          g_iap2_status = REC_CHANLLENGE_RESPONSE;
      }
      break;
      
    case REC_CHANLLENGE_RESPONSE:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, CHALLENGE_RESPONSE_LEN);            
      b_ret &= check_challenge_response();
      
      if(b_ret)
      {
          start_iap2_sequence(iap2_in_buffer, certification_tx_len);
          g_iap2_status = REC_CHALLENGE_ACK;
      }
      
      break;
      
    case REC_CHALLENGE_ACK:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IAP2_ACK_LEN);
      
      if(b_ret)
      {
          g_iap2_status = REC_START_IDENTIFICATION;
      }
      break;
      
    case REC_START_IDENTIFICATION:
      //natural_rev_len = udd_ep_receive_natural(IAP2_EP_OUT & 0x0f, iap2_out_buffer);
      
      if((natural_rev_len == START_IDENTIFICATION_LEN) && (iap2_out_buffer[13] == 0x1d) && (iap2_out_buffer[14] == 0x00))
      {
          b_ret &= check_start_identification();
      
          if(b_ret)
          {
              if(certification_tx_len > IAP2_PACKET_LEN)
              {
                  iap2_trans_in_ptr = &(iap2_in_buffer[0]);
                  start_iap2_sequence(iap2_trans_in_ptr, certification_tx_len);
                  iap2_trans_in_ptr += IAP2_PACKET_LEN;
                  certification_tx_len -= IAP2_PACKET_LEN;
                  udd_enable_endpoint_interrupt((IAP2_EP_IN&0x0f));
                  udd_enable_in_send_interrupt((IAP2_EP_IN&0x0f));
              }
              else
              {
                  start_iap2_sequence(iap2_in_buffer, certification_tx_len);
                  g_iap2_status += 1;
              }
          }
      }
      
      else if((natural_rev_len == AUTHEN_SUCCESS_LEN)  && (iap2_out_buffer[13] == 0xaa) && (iap2_out_buffer[14] == 0x05))
      {
          b_ret &= check_authen_success();
      
          if(b_ret)
          {
              start_iap2_sequence(iap2_in_buffer, IAP2_ACK_LEN);
              g_iap2_status += 2;
          }
      }                                                             
      else
      {
          b_ret = false;
      }
        
      
      break;
      
    case REC_START_IDENTIFICATION_ACK:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IAP2_ACK_LEN);
      
      if(b_ret)
      {
          g_iap2_status += 1;
      }
      break;
      
    case AUTHEN_SUCCESS_ACK:
      //natural_rev_len = udd_ep_receive_natural(IAP2_EP_OUT & 0x0f, iap2_out_buffer);
      
      if((natural_rev_len == START_IDENTIFICATION_LEN) && (iap2_out_buffer[13] == 0x1d) && (iap2_out_buffer[14] == 0x00))
      {
          b_ret &= check_start_identification();
      
          if(b_ret)
          {
              if(certification_tx_len > IAP2_PACKET_LEN)
              {
                  iap2_trans_in_ptr = &(iap2_in_buffer[0]);
                  start_iap2_sequence(iap2_trans_in_ptr, certification_tx_len);
                  iap2_trans_in_ptr += IAP2_PACKET_LEN;
                  certification_tx_len -= IAP2_PACKET_LEN;
                  udd_enable_endpoint_interrupt((IAP2_EP_IN&0x0f));
                  udd_enable_in_send_interrupt((IAP2_EP_IN&0x0f));
              }
              else
              {
                  start_iap2_sequence(iap2_in_buffer, certification_tx_len);
                  g_iap2_status += 1;
              }
          }
      }
      
      else if((natural_rev_len == AUTHEN_SUCCESS_LEN)  && (iap2_out_buffer[13] == 0xaa) && (iap2_out_buffer[14] == 0x05))
      {
          b_ret &= check_authen_success();
      
          if(b_ret)
          {
              start_iap2_sequence(iap2_in_buffer, IAP2_ACK_LEN);
              g_iap2_status += 2;
          }
      }
      else
      {
          b_ret = false;
      }                                                                                        
                                                                                              
      break;
      
    case REC_START_IDENTIFICATION_ACK_AGAIN:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IAP2_ACK_LEN);
      
      if(b_ret)
      {
          g_iap2_status += 1;
      }
      break;  
      
    case REC_IDENTIFICATION_ACCEPT:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IDENTIFICATION_ACCETP_LEN);
      //b_ret &= start_power_updates(0x1d,0x02);
      b_ret &= check_indentification();
      if(b_ret)
      {
          //start_iap2_sequence(iap2_in_buffer, START_POWER_UPDATES_LEN);
          //g_iap2_status = REC_EA_SESSION_START;
          start_iap2_sequence(iap2_in_buffer, 9);
          ep4_in_ptr = NULL;
          ep3_out_ptr = NULL;
          g_jpm_start_mfi = MFI_AUTH_COMPLETE;
          g_iap2_status = IAP2_IDLE;   
          udd_disable_endpoint_interrupt((IAP2_EP_OUT&0x0f));
          udd_disable_out_received_interrupt((IAP2_EP_OUT&0x0f));
          
      }
      break;
      
    case REC_EA_SESSION_START:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IAP2_ACK_LEN);
      if(b_ret)
      {
          iap2_packet_ack_num = iap2_out_buffer[5];
          g_iap2_status = REC_DATA_COMMAND;
      }
      break;
      
    case REC_DATA_COMMAND:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, 21);
      if(b_ret)
      {
          stop_power_updates();
          start_iap2_sequence(iap2_in_buffer, 16);
          g_iap2_status = SEND_IAP2_ACK;
      }
      break;
      
    case SEND_IAP2_ACK:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IAP2_ACK_LEN);
      if(b_ret)
      {
          ep4_in_ptr = NULL;
          ep3_out_ptr = NULL;
          g_jpm_start_mfi = MFI_AUTH_COMPLETE;
          g_iap2_status = IAP2_IDLE;   
     
          udd_disable_endpoint_interrupt((IAP2_EP_OUT&0x0f));
          udd_disable_out_received_interrupt((IAP2_EP_OUT&0x0f));                  
      }
      break;
      
    case CHARGE_SET:
      b_ret &= receive_iap2_pkt(iap2_out_buffer, IAP2_ACK_LEN);
      iap2_packet_ack_num = iap2_out_buffer[5];
        
      g_iap2_status = IAP2_IDLE;
      ep4_in_ptr = NULL;
      ep3_out_ptr = NULL;
      
      g_jpm_start_stop_charge = 0;
      
      if(b_reset_flag)
      {
          b_ret = false;
      }
              
      break;
      
    default:
      b_ret = false;
      break;
    }
    
    if(!b_ret)
    {
        g_iap2_status = IAP2_IDLE;
        g_jpm_start_mfi = MFI_NO_AUTH;                  
    }
}*/

/*static void get_init_sequence(void)
{
    common_mem_cpy(auth_init_data, iap2_in_buffer, AUTH_INIT_DATA_LEN);
}*/

static bool verify_init_sequence(uint32_t auth_data_len)
{
    uint8_t i;
    
    if(auth_data_len != AUTH_INIT_DATA_LEN)
    {
        return false;
    }
    
    for(i = 0; i < AUTH_INIT_DATA_LEN; i++)
    {
        if(iap2_out_buffer[i] != auth_init_data[i])
        {
            return false;
        }
    }
    
    return true;
}

static void get_sync_packet(void)
{
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    iap2_in_buffer[2] = 0x00;
    iap2_in_buffer[3] = 0x17;
    iap2_in_buffer[4] = 0x80;
    iap2_in_buffer[5] = 0x00;
    iap2_in_buffer[6] = 0x00;
    iap2_in_buffer[7] = 0x00;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
    iap2_in_buffer[9] = IAP2_LINK_VERSION;
    iap2_in_buffer[10] = MAX_NUM_OUTSTANDING_PACKET;
    iap2_in_buffer[11] = MAX_PACKET_LEN / 256;
    iap2_in_buffer[12] = MAX_PACKET_LEN % 256;
    iap2_in_buffer[13] = RETRANSMISSION_TIMEOUT / 256;
    iap2_in_buffer[14] = RETRANSMISSION_TIMEOUT % 256;
    iap2_in_buffer[15] = CUMULATIVE_ACK_TIMEOUT / 256;
    iap2_in_buffer[16] = CUMULATIVE_ACK_TIMEOUT % 256;
    iap2_in_buffer[17] = MAX_NUM_RETRANSMISSION;
    iap2_in_buffer[18] = MAX_CUMULATIVE_ACK;
    iap2_in_buffer[19] = CONTROL_SESSION_ID;
    iap2_in_buffer[20] = CONTROL_SESSION_TYPE;
    iap2_in_buffer[21] = SESSION_VERSION;
    //iap2_in_buffer[22] = EA_SESSION_ID;
    //iap2_in_buffer[23] = EA_SESSION_TYPE;
    //iap2_in_buffer[24] = SESSION_VERSION;
    iap2_in_buffer[22] = iap2_get_checksum(iap2_in_buffer + 9, 22 - 9);
    
    iap2_packet_sequence_num = 0;
}

static uint8_t iap2_get_checksum(uint8_t *data_ptr, uint16_t len)
{
    uint16_t i;
    uint8_t sum = 0;
    
    for(i = 0; i < len; i++)
    {
        sum += *data_ptr++;
    }
    
    return (uint8_t)(((~sum) & 0xff) + 1);
}

static bool check_sync_packet(uint32_t sync_pkt_len)
{
    uint8_t check_sum = 0;
    uint8_t i;
 
    if(sync_pkt_len != SYNC_PACKET_LEN)
    {
        return false;
    }
    
    if(iap2_out_buffer[9] == sync_verify_packet[0])
    {
        check_sum++;
    }
    
    for(i = 0; i < 2; i++)
    {
        if(iap2_out_buffer[13 + i] == sync_verify_packet[4 + i])
        {
            check_sum++;
        }
    }
    
    for(i = 0; i < 5; i++)
    {
        if(iap2_out_buffer[17 + i] == sync_verify_packet[8 + i])
        {
            check_sum++;
        }
    }
    
    if(check_sum == 8)
    {
        iap2_packet_ack_num = iap2_out_buffer[5];
        return true;
    }
    
    return false;
}

static void get_iap2_ack(void)
{
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    iap2_in_buffer[2] = 0x00;
    iap2_in_buffer[3] = 9;
    iap2_in_buffer[4] = 0x40;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_packet_ack_num;
    iap2_in_buffer[7] = 0x00;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
}

static bool verify_iap2_certification(uint32_t authe_certification_len)
{
    uint8_t auth_buf[1024] = {0};
    uint16_t page_num, i, j, packet_len, len;
    
    if(authe_certification_len != AUTHEN_CERTIFICATION_PACKET_LEN)
    {
        return false;
    }
    
    if(iap2_out_buffer[13] != 0xaa)
    {
        return false;
    }
    
    if(iap2_out_buffer[14] != 0x00)
    {
        return false;
    }
    
    //read data from MFI IC via IIC
    mop_Api_iic_read_data(0x30, auth_buf, 2);
    
    len = (auth_buf[0] << 8) + auth_buf[1];
    if(len > 1024)
    {
        len = 1024;
    }
    page_num = len / MFI_IC_PAGE_SIZE;
    if(len % 128)
    {
        page_num ++;
    }
    
    j = 0x31;
    for(i = 0; i < page_num; i++)
    {
        mop_Api_iic_read_data(j, auth_buf + (128 * i), 128);
        j++;
    }
    
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    packet_len = 10 + 10 + len;
    iap2_in_buffer[2] = packet_len / 256;
    iap2_in_buffer[3] = packet_len % 256;
    iap2_in_buffer[4] = 0x40;
    if(iap2_packet_sequence_num < 255)
        iap2_packet_sequence_num ++;
    else
        iap2_packet_sequence_num = 0;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_out_buffer[5];
    iap2_in_buffer[7] = 0x0a;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
    iap2_in_buffer[9] = 0x40;
    iap2_in_buffer[10] = 0x40;
    packet_len = 10 + len;
    iap2_in_buffer[11] = packet_len / 256;
    iap2_in_buffer[12] = packet_len % 256;
    iap2_in_buffer[13] = 0xaa;
    iap2_in_buffer[14] = 0x01;
    packet_len = 4 + len;
    iap2_in_buffer[15] = packet_len / 256;
    iap2_in_buffer[16] = packet_len % 256;
    iap2_in_buffer[17] = 0x00;
    iap2_in_buffer[18] = 0x00;
    
    common_mem_cpy(auth_buf, iap2_in_buffer + 19, len);
    /*for(i = 0; i < len; i++)
    {
        iap2_in_buffer[19 + i] = auth_buf[i];
    }*/
    iap2_in_buffer[19 + len] = iap2_get_checksum(iap2_in_buffer + 9, 10 + len);
    
    certification_tx_len = 20 + len;
    if(certification_tx_len != 928)
    {
        return false;
    }
    return true;
}

static bool check_challenge_response(uint32_t challenge_response_len)
{
    uint8_t auth_buf[150];
    uint16_t len, packet_len;
    
    if(challenge_response_len != CHALLENGE_RESPONSE_LEN)
    {
        return false;
    }
    
    if(iap2_out_buffer[13] != 0xaa)
    {
        return false;
    }
    if(iap2_out_buffer[14] != 0x02)
    {
        return false;
    }
    
    len = (iap2_out_buffer[15] << 8) + iap2_out_buffer[16] - 4;
    if(len > 1024 + 30 - 19)
    {
        len = 1024 + 30 - 19;
    }
    auth_buf[0] = len / 256;
    auth_buf[1] = len % 256;
    
    mop_Api_iic_write_data(0x20, auth_buf, 2);
    mop_Api_iic_write_data(0x21, iap2_out_buffer + 19, len);
    
    auth_buf[0] = 0x01;
    
    mop_Api_iic_write_data(0x10, auth_buf, 1);
    auth_buf[0] = 0;
    while(0 == auth_buf[0])
    {      
        mop_Api_iic_read_data(0x10, auth_buf, 1);
        mop_Api_iic_read_data(0x05, auth_buf + 1, 1);
        auth_buf[0] = auth_buf[0] & 0x10;
    }
    mop_Api_iic_read_data(0x11, auth_buf, 2);
    len = (auth_buf[0] << 8) + auth_buf[1];
    if(len > 150)
    {
        len = 150;
    }
    mop_Api_iic_read_data(0x12, auth_buf, len);
    
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    packet_len = 10 + 10 + len;
    iap2_in_buffer[2] = packet_len / 256;
    iap2_in_buffer[3] = packet_len % 256;
    iap2_in_buffer[4] = 0x40;
    if(iap2_packet_sequence_num < 255)
        iap2_packet_sequence_num++;
    else
        iap2_packet_sequence_num = 0;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_out_buffer[5];
    iap2_in_buffer[7] = 0x0a;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
    iap2_in_buffer[9] = 0x40;
    iap2_in_buffer[10] = 0x40;
    packet_len = 10 + len;
    iap2_in_buffer[11] = packet_len / 256;
    iap2_in_buffer[12] = packet_len % 256;
    iap2_in_buffer[13] = 0xaa;
    iap2_in_buffer[14] = 0x03;
    packet_len = 4 + len;
    iap2_in_buffer[15] = packet_len / 256;
    iap2_in_buffer[16] = packet_len % 256;
    iap2_in_buffer[17] = 0x00;
    iap2_in_buffer[18] = 0x00;
    
    common_mem_cpy(auth_buf, iap2_in_buffer + 19, len);
    /*for(i = 0; i < len; i++)
    {
        iap2_in_buffer[i + 19] = auth_buf[i];
    }*/
    iap2_in_buffer[19 + len] = iap2_get_checksum(iap2_in_buffer + 9, 10 + len);
    certification_tx_len = 20 + len;
    if(certification_tx_len != 148)
    {
        return false;
    }
    
    return true;
}

static uint16_t cpy_str_2_id(uint8_t* str, uint16_t id_offset, uint8_t index)
{
    uint16_t j = make_id_info_str(str, id_info_str, index);
    common_mem_cpy(id_info_str, iap2_in_buffer + id_offset, j);
    return id_offset + j;
}

static bool check_start_identification(uint32_t start_identification_len)
{
    uint16_t len, packet_len, i;
    if(start_identification_len != START_IDENTIFICATION_LEN)
    {
        return false;
    }
    
    if(iap2_out_buffer[13] != 0x1d)
    {
        return false;
    }
    if(iap2_out_buffer[14] != 0x00)
    {
        return false;
    }
    
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    iap2_in_buffer[4] = 0x40;
    if(iap2_packet_sequence_num < 255)
        iap2_packet_sequence_num++;
    else
        iap2_packet_sequence_num = 0;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_out_buffer[5];
    iap2_in_buffer[7] = 0x0a;
    iap2_in_buffer[9] = 0x40;
    iap2_in_buffer[10] = 0x40;
    iap2_in_buffer[13] = 0x1d;
    iap2_in_buffer[14] = 0x01;
    
    i = 15;
   
    i = cpy_str_2_id(USBD_PRODUCT_STRING, i, 0);
    /*for(j = 0; j < make_id_info_str(USBD_PRODUCT_STRING, id_info_str, 0); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    
    i = cpy_str_2_id(g_part_number, i, 1);
    /*for(j = 0; j < make_id_info_str(USBD_MODEL_STRING, id_info_str, 1); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    
    i = cpy_str_2_id(USBD_MANUFACTURER_STRING, i, 2);
    /*for(j = 0; j < make_id_info_str(USBD_MANUFACTURER_STRING, id_info_str, 2); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    //modify_sn_code();
    i = cpy_str_2_id(g_modify_code + 4, i, 3);
    /*for(j = 0; j < make_id_info_str(USBD_SERIALNUMBER_HS_STRING, id_info_str, 3); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    
    i = cpy_str_2_id(SOFTWARE_VERSION_STRING, i, 4);
    /*for(j = 0; j < make_id_info_str(SOFTWARE_VERSION_STRING, id_info_str, 4); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    
    i = cpy_str_2_id(HARDWARE_VERSION_STRING, i, 5);
    /*for(j = 0; j < make_id_info_str(HARDWARE_VERSION_STRING, id_info_str, 5); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    
    if((ex_status.ex_power_type == P_SRC_PC_PRE) && (b_need_to_verify == true))
    {
        i += common_mem_cpy(id_info_message_sent_by_accessory, iap2_in_buffer + i, sizeof(id_info_message_sent_by_accessory));
    }
    else
    {
        i += common_mem_cpy(id_info_message_sent_by_accessory_without_launch, iap2_in_buffer + i, sizeof(id_info_message_sent_by_accessory_without_launch));
    }
    /*for(j = 0; j < sizeof(id_info_message_sent_by_accessory); j++)
    {
        iap2_in_buffer[i++] = id_info_message_sent_by_accessory[j];
    }*/
    
    i += common_mem_cpy(id_info_message_rec_from_device, iap2_in_buffer + i, sizeof(id_info_message_rec_from_device));
    /*for(j = 0; j < sizeof(id_info_message_rec_from_device); j++)
    {
        iap2_in_buffer[i++] = id_info_message_rec_from_device[j];
    }*/
    
    i += common_mem_cpy(id_info_power_source_type, iap2_in_buffer + i, sizeof(id_info_power_source_type));
    /*for(j = 0; j < sizeof(id_info_power_source_type); j++)
    {
        iap2_in_buffer[i++] = id_info_power_source_type[j];
    }*/
    
    i += common_mem_cpy(id_info_max_current_drawn, iap2_in_buffer + i, sizeof(id_info_max_current_drawn));
    /*for(j = 0; j < sizeof(id_info_max_current_drawn); j++)
    {
        iap2_in_buffer[i++] = id_info_max_current_drawn[j];
    }*/
    
    i += common_mem_cpy(id_info_supported_accessory_protocol, iap2_in_buffer + i, sizeof(id_info_supported_accessory_protocol));
    /*for(j = 0; j < sizeof(id_info_supported_accessory_protocol); j++)
    {
        iap2_in_buffer[i++] = id_info_supported_accessory_protocol[j];
    }*/
    
    i = cpy_str_2_id(USBD_CURRENT_LANG_STRING, i, 12);
    /*for(j = 0; j < make_id_info_str(USBD_CURRENT_LANG_STRING, id_info_str, 12); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    
    i = cpy_str_2_id(USBD_SUPPORT_LANG_STRING, i, 13);
    /*for(j = 0; j < make_id_info_str(USBD_SUPPORT_LANG_STRING, id_info_str, 13); j++)
    {
        iap2_in_buffer[i++] = id_info_str[j];
    }*/
    
    i += common_mem_cpy(id_info_host_trans_component, iap2_in_buffer + i, sizeof(id_info_host_trans_component));
    /*for(j = 0; j < sizeof(id_info_host_trans_component); j++)
    {
        iap2_in_buffer[i++] = id_info_host_trans_component[j];
    }*/
    
    packet_len = i + 1;
    iap2_in_buffer[2] = packet_len / 256;
    iap2_in_buffer[3] = packet_len % 256;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
    len = packet_len - 10;
    iap2_in_buffer[11] = len / 256;
    iap2_in_buffer[12] = len % 256;
    iap2_in_buffer[i] = iap2_get_checksum(iap2_in_buffer + 9, i - 9);
    certification_tx_len = packet_len;
    
    return true;
}

static uint16_t make_id_info_str(uint8_t *src, uint8_t *dst, uint8_t index)
{
    uint16_t len = 0;
    uint16_t i = 0;
    
    while(src[i] != NULL)
    {
        dst[4 + i] = src[i];
        i++;
        len ++;
    }
    dst[4 + i] = NULL;
    len++;
    len += 4;
    
    dst[0] = 0x00;
    dst[1] = (uint8_t)(len & 0x00ff);
    dst[2] = 0x00;
    dst[3] = index;
    
    return len;
}

static bool check_iap_ack(uint8_t a, uint8_t b)
{
    if(iap2_out_buffer[13] != a)
    {
        return false;
    }
    if(iap2_out_buffer[14] != b)
    {
        return false;
    }
    
    iap2_packet_ack_num = iap2_out_buffer[5];
    
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    iap2_in_buffer[2] = 0x00;
    iap2_in_buffer[3] = IAP2_ACK_LEN;
    iap2_in_buffer[4] = 0x40;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_packet_ack_num;
    iap2_in_buffer[7] = 0x00;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
  
    return true;
}

static bool start_power_updates()
{
    iap2_packet_ack_num = iap2_out_buffer[5];
    iap2_in_buffer[0]  = START_PACKET_MSB;
    iap2_in_buffer[1]  = START_PACKET_LSB;
    iap2_in_buffer[2]  = 0x00;
    iap2_in_buffer[3]  = 24;
    iap2_in_buffer[4]  = 0x40;
    if(iap2_packet_sequence_num < 255)
        iap2_packet_sequence_num++;
    else
        iap2_packet_sequence_num = 0;
    iap2_in_buffer[5]  = iap2_packet_sequence_num;
    iap2_in_buffer[6]  = iap2_packet_ack_num;
    iap2_in_buffer[7]  = 0x0a;
    iap2_in_buffer[8]  = iap2_get_checksum(iap2_in_buffer, 8);
    iap2_in_buffer[9]  = 0x40;
    iap2_in_buffer[10] = 0x40;
    iap2_in_buffer[11] = 0x00;
    iap2_in_buffer[12] = 14;
    iap2_in_buffer[13] = 0xae;
    iap2_in_buffer[14] = 0x00;
    iap2_in_buffer[15] = 0x00;
    iap2_in_buffer[16] = 0x04;
    iap2_in_buffer[17] = 0x00;
    iap2_in_buffer[18] = 0x00;
    iap2_in_buffer[19] = 0x00;
    iap2_in_buffer[20] = 0x04;
    iap2_in_buffer[21] = 0x00;
    iap2_in_buffer[22] = 0x01;
    iap2_in_buffer[23] = iap2_get_checksum(iap2_in_buffer+9,14);    
    return true;
}

static void stop_power_updates(void)
{
    iap2_packet_ack_num = iap2_out_buffer[5];
    
    iap2_in_buffer[0]  = START_PACKET_MSB;
    iap2_in_buffer[1]  = START_PACKET_LSB;
    iap2_in_buffer[2]  = 0x00;
    iap2_in_buffer[3]  = 16;
    iap2_in_buffer[4]  = 0x40;
    if(iap2_packet_sequence_num < 255)
        iap2_packet_sequence_num++;
    else
        iap2_packet_sequence_num = 0;
    iap2_in_buffer[5]  = iap2_packet_sequence_num;
    iap2_in_buffer[6]  = iap2_packet_ack_num;
    iap2_in_buffer[7]  = 0x0a;
    iap2_in_buffer[8]  = iap2_get_checksum(iap2_in_buffer, 8);
    iap2_in_buffer[9]  = 0x40;
    iap2_in_buffer[10] = 0x40;
    iap2_in_buffer[11] = 0x00;
    iap2_in_buffer[12] = 6;
    iap2_in_buffer[13] = 0xae;
    iap2_in_buffer[14] = 0x02;
    iap2_in_buffer[15] = iap2_get_checksum(iap2_in_buffer+9,6);
}

/*static bool check_authen_success(void)
{
    if(iap2_out_buffer[13] != 0xaa)
    {
        return false;
    }
    if(iap2_out_buffer[14] != 0x05)
    {
        return false;
    }
    
    iap2_packet_ack_num = iap2_out_buffer[5];
    
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    iap2_in_buffer[2] = 0x00;
    iap2_in_buffer[3] = IAP2_ACK_LEN;
    iap2_in_buffer[4] = 0x40;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_packet_ack_num;
    iap2_in_buffer[7] = 0x00;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
  
    return true;
}*/

/*static bool check_indentification(void)
{
    if(iap2_out_buffer[13] != 0x1d)
    {
        return false;
    }
    if(iap2_out_buffer[14] != 0x02)
    {
        return false;
    }
    
    iap2_packet_ack_num = iap2_out_buffer[5];
    
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    iap2_in_buffer[2] = 0x00;
    iap2_in_buffer[3] = IAP2_ACK_LEN;
    iap2_in_buffer[4] = 0x40;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_packet_ack_num;
    iap2_in_buffer[7] = 0x00;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
  
    return true;
}*/

static void set_charge_setting(uint8_t charge_mode, uint8_t charge_flag)
{
    uint16_t charge_voltage;
    
    switch(charge_mode)
    {
    case CHARGE_MODE_FULL:
      charge_voltage = CHARGE_VOLTAGE_1000;
      break;
    case CHARGE_MODE_NORMAL:
      charge_voltage = CHARGE_VOLTAGE_800;
      break;
    case CHARGE_MODE_HALF:
      charge_voltage = CHARGE_VOLTAGE_500;
      break;
    case CHARGE_MODE_OFF:
    default:
      charge_flag = 0;
      charge_voltage = 0;
      break;       
    }
    
    iap2_in_buffer[0] = START_PACKET_MSB;
    iap2_in_buffer[1] = START_PACKET_LSB;
    iap2_in_buffer[2] = 0;
    iap2_in_buffer[3] = 27; 
    iap2_in_buffer[4] = 0x40;
    if(iap2_packet_sequence_num < 255)
        iap2_packet_sequence_num++;
    else
        iap2_packet_sequence_num = 0;
    iap2_in_buffer[5] = iap2_packet_sequence_num;
    iap2_in_buffer[6] = iap2_packet_ack_num;
    iap2_in_buffer[7] = CONTROL_SESSION_ID;
    iap2_in_buffer[8] = iap2_get_checksum(iap2_in_buffer, 8);
    iap2_in_buffer[9] = 0x40;
    iap2_in_buffer[10] = 0x40;
    iap2_in_buffer[11] = 0;
    iap2_in_buffer[12] = 17;
    iap2_in_buffer[13] = 0xae;
    iap2_in_buffer[14] = 0x03;
    
    iap2_in_buffer[15] = 0x00;
    iap2_in_buffer[16] = 0x06;
    iap2_in_buffer[17] = 0x00;
    iap2_in_buffer[18] = 0x00;
    iap2_in_buffer[19] = charge_voltage / 256;
    iap2_in_buffer[20] = charge_voltage % 256;
    iap2_in_buffer[21] = 0x00;
    iap2_in_buffer[22] = 0x05;
    iap2_in_buffer[23] = 0x00;
    iap2_in_buffer[24] = 0x01;
    iap2_in_buffer[25] = charge_flag;
    iap2_in_buffer[26] = iap2_get_checksum(iap2_in_buffer + 9, 26 - 9);
}

static void start_apple_lunch(void)
{
    uint8_t i;
    iap2_packet_ack_num = iap2_out_buffer[5];
    
    iap2_in_buffer[0]  = START_PACKET_MSB;
    iap2_in_buffer[1]  = START_PACKET_LSB;
        
    iap2_in_buffer[4]  = 0x40;
    if(iap2_packet_sequence_num < 255)
        iap2_packet_sequence_num++;
    else
        iap2_packet_sequence_num = 0;
    iap2_in_buffer[5]  = iap2_packet_sequence_num;
    iap2_in_buffer[6]  = iap2_packet_ack_num;
    iap2_in_buffer[7]  = 0x0a;
    
    iap2_in_buffer[9]  = 0x40;
    iap2_in_buffer[10] = 0x40;
    iap2_in_buffer[13] = 0xea;
    iap2_in_buffer[14] = 0x02;
    
    i = 15;
   
    i = cpy_str_2_id(APP_BUNDLE_ID, i, 0); //APPLE_LUNCH_STRING
    i++;
    
    iap2_in_buffer[2]  = i/256;
    iap2_in_buffer[3]  = i%256;
    iap2_in_buffer[8]  = iap2_get_checksum(iap2_in_buffer, 8);
    
    iap2_in_buffer[11] = (i-10)/256;
    iap2_in_buffer[12] = (i-10)%256;
    
    iap2_in_buffer[i-1] = iap2_get_checksum(iap2_in_buffer+9,i-10);
//    iap2_in_buffer[15] = 0x00;
//    iap2_in_buffer[16] = 0x00;
//    iap2_in_buffer[17] = ;
//    iap2_in_buffer[18] = ;
//    iap2_in_buffer[19] = 'c';
//    iap2_in_buffer[20] = 'o';
//    iap2_in_buffer[21] = 'm';
//    iap2_in_buffer[22] = '.'
//    iap2_in_buffer
//    iap2_in_buffer
}

